#include <pybind11/pybind11.h>
#include <pybind11/stl.h>  // 用于自动转换 vector<string> 等 STL 类型
#include <pybind11/numpy.h>
#include "tool.h"
#include "myfilter.hpp"

namespace py = pybind11;

// 辅助函数： numpy 数组 (n, 2) 转换为 vector<Point<T>>
template<typename T>
std::vector<Point<T>> ndarray_to_points(const py::array_t<T>& arr)
{
    if (arr.ndim() != 2 || arr.shape(1) != 2){
        throw std::runtime_error("The input must be a two-dimensional array with shape (n, 2)");
    }

    // 获取数组信息（形状、步长、数据指针）
    py::buffer_info buf = arr.request();

    // 获取步长（每个维度的内存跨度，单位：元素大小）
    // strides[0]：行之间的步长（下一行第一个元素与当前行第一个元素的距离）
    // strides[1]：列之间的步长（同一行下一列元素与当前元素的距离）
    const ssize_t row_stride = buf.strides[0] / sizeof(T);  // 转换为元素数量
    const ssize_t col_stride = buf.strides[1] / sizeof(T);

    // 转换数据
    std::vector<Point<T>> points;
    points.reserve(buf.shape[0]);  // 预分配空间
    
    // 原始数据指针
    const T* ptr = static_cast<const T*>(buf.ptr);

    for(size_t i = 0; i< buf.shape[0]; i++)
    {
        // 计算第 i 行第 0 列和第 1 列的索引（考虑步长，支持非连续数组）
        const T* row_start = ptr + i * row_stride;
        T x = row_start[0 * col_stride]; // 第 0 列
        T y = row_start[1 * col_stride]; // 第 1 列
        points.emplace_back(x, y);
    }
    return points;
}

// 辅助函数：将 vector<int> 转换为 numpy 数组
py::array_t<int> vector_to_ndarray(const std::vector<int>& vec) {
    return py::array_t<int>(vec.size(), vec.data());
}

// 绑定函数：将 C++ 类映射到 Python
PYBIND11_MODULE(tools_lib, m) {  // tools_lib 是生成的 Python 库名
    m.doc() = "Tool library wrapped with pybind11";  // 模块文档字符串

    // 绑定 Tool 类
    py::class_<Tools>(m, "Tools")
        // 绑定构造函数（支持两种重载）
        .def(py::init<int, string>(), "Constructor: receives num and user") // 构造函数：接收 num 和 user
        .def(py::init<const vector<string> &>(), "Constructor: receives a vector of strings uv")  // 构造函数：接收字符串向量 uv
        // 暴露成员变量（可选，建议通过方法访问，这里为了示例直接暴露）
        .def_readwrite("num", &Tools::num, "Integer member num")  // 整数成员 num
        .def_readwrite("user", &Tools::user, "String member user")  // 字符串成员 user
        .def_readwrite("uv", &Tools::uv, "String vector member uv")  // 字符串向量成员 uv
        // 绑定成员方法
        .def("join_str", &Tools::join_str, "Concatenate all strings in uv and return");  // 拼接 uv 中的所有字符串并返回
    
    // 绑定 filter_point 函数
    // 绑定整数类型
    m.def("filter_point_int",
        [](const py::array_t<int>& p1_arr, const py::array_t<int>& p2_arr){
            auto p1 = ndarray_to_points<int>(p1_arr);  // 转换 p1 为 vector<Point<int>>
            auto p2 = ndarray_to_points<int>(p2_arr);  // 转换 p2 为 vector<Point<int>>
            auto result = filter_point<int>(p1, p2);   // 调用整数版本的 filter_point
            return vector_to_ndarray(result);          // 转换结果为 numpy 数组
        },
        "filter int point",
        py::arg("p1"), py::arg("p2")  // 显式指定参数名（Python 中调用更清晰）
    );

    // 绑定 filter_point 函数（浮点版本）
    m.def("filter_point_float", 
        [](const py::array_t<double>& p1_arr, const py::array_t<double>& p2_arr) {
            auto p1 = ndarray_to_points<double>(p1_arr);  // 转换 p1 为 vector<Point<double>>
            auto p2 = ndarray_to_points<double>(p2_arr);  // 转换 p2 为 vector<Point<double>>
            auto result = filter_point<double>(p1, p2);   // 调用浮点版本的 filter_point
            return vector_to_ndarray(result);             // 转换结果为 numpy 数组
        }, 
        "filter float point",
        py::arg("p1"), py::arg("p2")  // 显式指定参数名
    );
}